package com.smsc.headend.task.engine.handler.impl.deviceconfig;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.smsc.headend.common.enums.PaymentSwithPreToPostOptGroupEnum;
import com.smsc.headend.common.enums.errorcode.TaskHandlerErrorCode;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.data.dto.DataItemDTO;
import com.smsc.headend.module.data.entity.RdMeterReadsLp;
import com.smsc.headend.module.data.enums.CollectionSource;
import com.smsc.headend.module.data.enums.OperationName;
import com.smsc.headend.module.protocol.DeviceData;
import com.smsc.headend.module.protocol.cosem.bo.CosemData;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.task.dto.TaskReadData;
import com.smsc.headend.module.task.enums.SubTaskStatus;
import com.smsc.headend.module.task.enums.TaskType;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskHandlerException;
import com.smsc.headend.task.engine.handler.TaskHandler;
import com.smsc.headend.task.engine.handler.impl.collection.strategy.CollectionStrategy;
import com.smsc.headend.task.engine.handler.impl.collection.strategy.CollectionStrategyFactory;
import com.smsc.headend.task.engine.service.DataProcessService;
import com.smsc.headend.task.engine.service.DeviceStatusService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.calc.RdEnergyDailyService;
import com.smsc.headend.task.engine.service.switchmode.RdPaymentModeToPostLpDataService;
import com.smsc.headend.task.engine.service.task.LogScheduleTaskDeviceService;
import com.smsc.headend.task.engine.service.task.impl.TaskServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * (业务实现)
 *
 * @author wjh
 * @date 2022/5/18 15:37.
 */
public class ReadLpForSwitchPaymentToPostHandler implements TaskHandler {

    @Autowired
    FeignAssetManagementService assetManagementService;
    @Autowired
    KafkaTemplate kafkaTemplate;
    @Autowired
    DataProcessService dataProcessService;
    @Autowired
    TaskDAO taskDAO;
    @Autowired
    TaskServiceImpl taskService;
    @Autowired
    AssetService assetService;
    @Autowired
    CollectionStrategyFactory collectionStrategyFactory;
    @Autowired
    LogScheduleTaskDeviceService logScheduleTaskDeviceService;
    @Autowired
    DeviceStatusService deviceStatusService;
    @Autowired
    RdEnergyDailyService rdEnergyDailyService;
    @Autowired
    RdPaymentModeToPostLpDataService rdPaymentModeToPostLpDataService;

    @Override
    public int handle(Task task, AtomicTask atomicTask, DeviceData deviceData, String actionResult) throws UDISTaskHandlerException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        if (!(deviceData instanceof CosemData)) {
            log.error("result={}, data={}", actionResult, deviceData);
            throw new UDISTaskHandlerException(TaskHandlerErrorCode.DEVICE_DATA_TYPE_NOT_SUPPORT.toString());
        }
        CosemData cosemData = (CosemData) deviceData;
        Map<String, Object> param = getParameterMap(task, atomicTask);
        Long lpTemplateId = MapUtil.getLong(param, "lpTemplateId");
        Long lpDataItemId = MapUtil.getLong(param, "dataItemId");
        if (lpTemplateId == null) {
            throw new UDISTaskHandlerException(TaskHandlerErrorCode.NO_LP_TEMPLATE_ID.toString());
        }
        List<DataItemDTO> dataItemCosemList = dataProcessService.getLoadProfileCaptureObjects(lpTemplateId, lpDataItemId, task.getMeterId());
        if (CollectionUtil.isEmpty(dataItemCosemList)) {
            throw new UDISTaskHandlerException(TaskHandlerErrorCode.INVALID_CONFIG_CAP_OBJECTS.toString());
        }
        log.debug("capture objects {}", JSONUtil.toJsonStr(dataItemCosemList));
        Meter meter = assetService.getMeterById(task.getMeterId());
        Boolean newVersionFlag = isNewDLMSGatewayData(cosemData);
        if (newVersionFlag) {
            //目前DLMS Gateway只支持单块电表的数据采集。即 Array长度只为1
            CosemData firstArrayElement = cosemData.getChildAt(0);
            String meterSnInDLMSGateway = firstArrayElement.getChildAt(0).getValue();
            //暂时不做profile的obis校验
//            String obis = firstArrayElement.getChildAt(1).getValue();
            if (!meter.getMfgSn().equalsIgnoreCase(meterSnInDLMSGateway)) {
                throw new UDISTaskHandlerException(TaskHandlerErrorCode.DLMS_GATEWAY_METER_SN_NOT_MATCH.toString());
            }
            cosemData = firstArrayElement.getChildAt(2);
        }
        CollectionSource collectionSource = getCollectionType(task);
        try {
            List<RdMeterReadsLp> meterReadsCol = new ArrayList<>();
            if (CollUtil.isNotEmpty(cosemData.getChild())) {
                meterReadsCol = dataProcessService.getRdMeterReadLpList(cosemData, lpTemplateId, lpDataItemId, dataItemCosemList, meter, collectionSource);
            }

            CollectionStrategy collectionStrategy = collectionStrategyFactory.getCollectionStrategy(collectionSource);
            //data filter
            List<RdMeterReadsLp> filteredRdMeterReadsLp = collectionStrategy.filterCollectionData(task, atomicTask, param, meterReadsCol);
            //save profile data
            TaskReadData readData = new TaskReadData();
            readData.setProfileId(lpDataItemId);
            readData.setTaskNo(task.getTaskNo());
            readData.setIsODR(task.getIsODR());
            if (StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.SwitchMeterPaymentPreToPost.name())) {
                Map<String, Object> parameterMap = getParameterMap(task, atomicTask);
                Long taskId = MapUtil.getLong(parameterMap, "taskId");
                assetManagementService.updateStepStatusToPost(taskId, task.getTaskNo(), task.getMeterId(), PaymentSwithPreToPostOptGroupEnum.ON_DEMAND_MONTHLY_BILLING_PROFILE.getOperationGroup().longValue(), SubTaskStatus.Success.getCode(), null, SystemClock.now() / 1000,
                        null, true);
            }
            if (CollUtil.isNotEmpty(filteredRdMeterReadsLp)) {
                if (StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.SwitchMeterPaymentPreToPost.name())) {
                    //保存随抄的曲线数据
                    rdPaymentModeToPostLpDataService.save(filteredRdMeterReadsLp, task.getTaskNo());
                }
                deviceStatusService.updateMeterReadDataStatus(meter);
                readData.setTaskMeterReads(filteredRdMeterReadsLp);
            } else {
            }
            //callback
            int result = collectionStrategy.callback(meter, readData, task, atomicTask, param, dataItemCosemList, filteredRdMeterReadsLp);
            return result;
        } catch (Exception e) {
            log.error("Unexpected error", e);
        }
        return -1;
    }

    private Boolean isNewDLMSGatewayData(CosemData cosemData) {
        if (!"Array".equalsIgnoreCase(cosemData.getType())) {
            return false;
        }
        if (cosemData.getChild() == null) {
            return false;
        }
        CosemData firstArrayElement = cosemData.getChildAt(0);
        if (firstArrayElement.getChild() == null || firstArrayElement.getChild().size() != 3) {
            return false;
        }
        //Structure 中第三个元素为Array类型 则为新版本DLMS Gateway的协议
        if ("Array".equalsIgnoreCase(firstArrayElement.getChildAt(2).getType())) {
            return true;
        }
        return false;
    }

    private CollectionSource getCollectionType(Task task) {

        if (TaskType.SwitchMeterPaymentPreToPost.toString().equalsIgnoreCase(task.getTaskType())) {
            return CollectionSource.ODR;
        }
        return null;
    }

    @Override
    public int handleFailure(Task task, AtomicTask atomicTask, String message) {
        log.info("error msg:{}", message);
        if (StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.SwitchMeterPaymentPreToPost.name())) {
            Map<String, Object> parameterMap = getParameterMap(task, atomicTask);
            Long taskId = MapUtil.getLong(parameterMap, "taskId");
            assetManagementService.updateStepStatusToPost(taskId, task.getTaskNo(), task.getMeterId(), PaymentSwithPreToPostOptGroupEnum.ON_DEMAND_MONTHLY_BILLING_PROFILE.getOperationGroup().longValue(), SubTaskStatus.Failed.getCode(), null, SystemClock.now() / 1000,
                    String.format("handle %s fail, [%s]", OperationName.forId(atomicTask.getOperationId()).getName(), message), false);
        }
        return -1;
    }
}
